/*
 * ============================================================================
 *
 *       Filename:  process.c
 *
 *    Description:  
 *
 *        Version:  1.0
 *        Created:  2014年08月25日 14时31分55秒
 *       Revision:  none
 *       Compiler:  gcc
 *
 *         Author:  jianxi sun (jianxi), ycsunjane@gmail.com
 *   Organization:  
 *
 * ============================================================================
 */
#include <stdio.h>
#include <stdlib.h>
/* sockaddr_in */
#include <netinet/in.h>

#include "system.h"
#include "resip.h"
#include "netlayer.h"
#include "sockarr.h"
#include "approc.h"
#include "process.h"
#include "apsync.h"

static int uuid_equ(char *src, char *dest)
{
	return !strncmp(src, dest, UUID_LEN - 1);
}

static void 
ap_status(struct ap_t *ap, struct msg_ap_status_t *apstatus,
	int len, int proto)
{
	assert(proto == MSG_PROTO_TCP);
	CHECK_MSGLEN(apstatus, len);
	if(ap->netrandom)
		CHECK_CHAP(apstatus, len, ap->netrandom);
	sys_debug("recive ap report status packet\n");
	sys_debug("%s\n", apstatus->status);

	char *cmd;
	unsigned char *smac;
	int cmdlen, total, needrestart;
	struct msg_ac_cmd_t *ctlcmd;

	smac = apstatus->header.smac;
	cmd = ap_sync(smac, apstatus->status, &needrestart);
	cmdlen = strlen(cmd) + 1;
	total = cmdlen + sizeof(struct msg_ac_cmd_t);
	ctlcmd = safe_malloc(total);
	sys_debug("cmd: %s, len: %d, total: %d\n", cmd, cmdlen, total);

	ap->netrandom = chap_get_random();
	fill_msg_header((void *)ctlcmd, MSG_AC_CMD, 
		&gconf.acuuid[0], ap->netrandom);

	ctlcmd->needrestart = needrestart;
	strcpy(ctlcmd->cmd, cmd);

	chap_fill_msg_md5((void *)ctlcmd, total,
		apstatus->header.random);
	sockarr_send(proto, ap->sock, smac, (void *)ctlcmd, total);

	free(cmd);
	free(ctlcmd);
}

static void 
ac_req_reg(struct ap_t *ap, int proto, 
	unsigned char *smac, uint32_t random)
{
	char *regcmd;
	int reglen, total, needrestart;
	struct msg_ac_req_reg_t *resp; 

	regcmd = resip_alloc(smac, &needrestart);
	sys_debug("needrestart: %d, cmd: %s\n", needrestart, regcmd);

	reglen = strlen(regcmd) + 1;
	total = reglen + sizeof(struct msg_ac_req_reg_t);
	resp = safe_malloc(total);

	/* generate random2 */
	ap->dllrandom = chap_get_random();
	fill_msg_header((void *)resp, MSG_AC_REQ_REG, 
		&gconf.acuuid[0], ap->dllrandom);

	resp->acaddr = gconf.addr;
	resp->needrestart = needrestart;
	strcpy(resp->regcmd, regcmd);

	/* calculate chap: 
	 * md5sum3 = packet + random1 + password */
	chap_fill_msg_md5((void *)resp, total, random);
	sockarr_send(proto, ap->sock, smac,
		(void *)resp, total);

	free(resp);
	free(regcmd);
}

static void ap_brd_resp(struct ap_t *ap, 
	struct msg_ap_brd_resp_t *msg, int len, int proto)
{
	if(len < sizeof(*msg)) {
		sys_warn("receive wrong ap response packet\n");
		return;
	}

	sys_debug("recive ap broad response packet: %d\n", len);
	/* XXX:first random is broadcast random, 
	 * ac broadcast random will change every argument.brditv
	 * so ap reg packet must be recive in argument.brditv */
	/* calculate: md5sum2 = packet + random0 + password
	 * compare md5sum1 with md5sum2*/
	CHECK_CHAP(msg, len, gconf.chaprandom);

	struct apdef_t apdef = {0};
	apdef.isreg = ntohl(msg->isreg);
	if(apdef.isreg && addr_equ(&msg->acaddr, &gconf.addr))
		apdef.ismine = 1;
	mac_to_macstr(msg->header.smac, apdef.macstr);
	ip_to_ipstr(&msg->acaddr, apdef.acaddr);
	
	if(ap_reg(&apdef, msg->apstatus)) {
		ac_req_reg(ap, proto, msg->header.smac, msg->header.random);
	}
}

int is_mine(struct msg_head_t *msg, int len)
{
	/* check packet len */
	if(len < sizeof(*msg)) {
		sys_err("receive ultrashort packet\n");
		return 0;
	}

	if(!uuid_equ(msg->acuuid, gconf.acuuid)) {
		sys_err("receive non-local ap response\n");
		return 0;
	}
	return 1;
}

void msg_proc(struct ap_hash_t *aphash, 
	struct msg_head_t *msg, int len, int proto)
{
	sys_debug("RECV: receive a packet len: %d, "
		"proto: %d\n", len, proto);
	if(!is_mine(msg, len)) return;

	switch(ntohl(msg->msg_type)) {
	case MSG_AP_BRD_RESP:
		ap_brd_resp(&aphash->ap, (void *)msg,
			len, proto);
		break;
	case MSG_AP_STATUS:
		ap_status(&aphash->ap, (void *)msg, 
			len, proto);
		break;
	default:
		sys_warn("RECV: receive unknow packet: %x\n",
			ntohl(msg->msg_type));
		break;
	}
}
